package com.googlecode.jyoutubeuploader.uploader;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;

import org.apache.log4j.Logger;

import com.google.gdata.client.youtube.YouTubeService;
import com.google.gdata.data.media.mediarss.MediaCategory;
import com.google.gdata.data.media.mediarss.MediaDescription;
import com.google.gdata.data.media.mediarss.MediaKeywords;
import com.google.gdata.data.media.mediarss.MediaTitle;
import com.google.gdata.data.youtube.VideoEntry;
import com.google.gdata.data.youtube.YouTubeMediaGroup;
import com.google.gdata.data.youtube.YouTubeNamespace;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.InvalidEntryException;
import com.google.gdata.util.ServiceException;
import com.googlecode.jyoutubeuploader.to.YouTubeArgumentsTo;
import com.googlecode.jyoutubeuploader.util.StatisticsCalculator;

public class YouTubeUploader implements Uploader{

	private YouTubeService service;
	private Logger logger;
	private YouTubeArgumentsTo ytArgs;
	private String uploadUrl;
	private ThrottledMediaFileSourceFactory throttledMediaFileSourceFactory;
	private StatisticsCalculator statisticsCalculator;

	public void doUpload() {
		VideoEntry newEntry = new VideoEntry();
		YouTubeMediaGroup mg = newEntry.getOrCreateMediaGroup();
		setTitle(mg);
		setCategory(mg);
		setKeywords(mg);
		setDesciption(mg);
		setPrivacy(mg);
		setDevTags(mg);
		setGeoCoordinates(mg);
		setMediaSource(newEntry);
		Date start = new Date();
		callUploadService(newEntry);
		printFinishedMessage(start);
	}

	private void setCredentials(String username, String password) {
		try {
			service.setUserCredentials(username, password);
		} catch (AuthenticationException e) {
			logger.fatal("Failed to set user credentials.", e);
		}
	}
	
	protected void printFinishedMessage(Date start) {
		long elapsedSeconds = statisticsCalculator.getElapsedSeconds(start);
		float estimatedSpeed = statisticsCalculator.getEstimatedSpeed(elapsedSeconds, ytArgs.getFile().length());
		logger.info("Finished upload process. Elapsed time was " + elapsedSeconds + " seconds. " +
				"Average speed was " + estimatedSpeed + "kbps.");
	}

	protected void callUploadService(VideoEntry newEntry) {
		try {
			logger.info("Starting upload process.");
			VideoEntry createdEntry = service.insert(new URL(uploadUrl), newEntry);
			printLinkToVideo(createdEntry);
		} catch (InvalidEntryException e) {
			logger.debug(e.getCodeName() + "  " + e.getLocation());
			e.printStackTrace();
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	protected void printLinkToVideo(VideoEntry createdEntry) {
		String message = createdEntry.getHtmlLink() == null ? "Could not retrieve URL of new video." 
				: createdEntry.getHtmlLink().getHref();
		logger.info("Link to new video is: " + message);
	}

	protected void setMediaSource(VideoEntry newEntry) {
		ThrottledMediaFileSource ms = throttledMediaFileSourceFactory.getThrottledMediaFileSource(ytArgs);
		newEntry.setMediaSource(ms);
	}

	private void setGeoCoordinates(YouTubeMediaGroup mg) {
		// TODO Implement this
		// newEntry.setGeoCoordinates(new GeoRssWhere(37.0,-122.0));
		// alternatively, one could specify just a descriptive string
		// newEntry.setLocation("Mountain View, CA");
	}

	private void setDevTags(YouTubeMediaGroup mg) {
		// TODO Implement this
		// mg.addCategory(new MediaCategory(YouTubeNamespace.DEVELOPER_TAG_SCHEME, "mydevtag"));
		// mg.addCategory(new MediaCategory(YouTubeNamespace.DEVELOPER_TAG_SCHEME, "anotherdevtag"));
	}

	protected void setPrivacy(YouTubeMediaGroup mg) {
		mg.setPrivate(ytArgs.isPrivate());
	}

	protected void setDesciption(YouTubeMediaGroup mg) {
		if (ytArgs.getDescription() != null){
			mg.setDescription(new MediaDescription());
			mg.getDescription().setPlainTextContent(ytArgs.getDescription());
		}
	}

	protected void setKeywords(YouTubeMediaGroup mg) {
		if (ytArgs.getKeywords() != null){
			mg.setKeywords(new MediaKeywords());
			for (String keyword : ytArgs.getKeywords()){
				mg.getKeywords().addKeyword(keyword);
			}
		}
	}

	protected void setCategory(YouTubeMediaGroup mg) {
		mg.addCategory(new MediaCategory(YouTubeNamespace.CATEGORY_SCHEME, ytArgs.getCategory().getTerm()));
	}

	protected void setTitle(YouTubeMediaGroup mg) {
		mg.setTitle(new MediaTitle());
		mg.getTitle().setPlainTextContent(ytArgs.getTitle());
	}

	public void setService(YouTubeService service) { this.service = service; }
	public void setLogger(Logger logger) { this.logger = logger; }
	public void setUploadUrl(String uploadUrl) { this.uploadUrl = uploadUrl; }
	public void setArgs(YouTubeArgumentsTo args) {
		this.ytArgs = args;
		setCredentials(ytArgs.getUsername(), ytArgs.getPassword());
	}
	public void setThrottledMediaFileSourceFactory(ThrottledMediaFileSourceFactory throttledMediaFileSourceFactory) {
		this.throttledMediaFileSourceFactory = throttledMediaFileSourceFactory;
	}
	public void setStatisticsCalculator(StatisticsCalculator statisticsCalculator) { this.statisticsCalculator = statisticsCalculator; }
}
